package com.zlzlib.log.config;

import com.zlzlib.common.util.IpUtil;
import com.zlzlib.common.util.JacksonUtil;
import com.zlzlib.log.bean.LogConstants;
import com.zlzlib.log.bean.enums.LogType;
import com.zlzlib.log.bean.po.LogPo;
import com.zlzlib.log.service.LogSaveService;
import com.zlzlib.log.tools.LogLibUtils;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

import static com.zlzlib.log.bean.LogConstants.DATE_FORMAT;


/**
 * @DateTime: 2023/3/8 16:48
 * @Author zlz
 * @Version 1.0
 */
@Aspect
@Component
@RequiredArgsConstructor
public class LogAspect {

    private final LogSaveService logService;

    @Pointcut("@annotation(com.zlzlib.log.config.LogOperation)")
    public void logPointCut() {
    }

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        return aroundDo(point);
    }

    private Object aroundDo(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        try {
            //执行方法
            Object result = point.proceed();
            //执行时长(毫秒)
            long time = System.currentTimeMillis() - beginTime;
            //保存日志
            saveLog(point, time, result, false);
            return result;
        } catch (Throwable e) {
            //执行时长(毫秒)
            long time = System.currentTimeMillis() - beginTime;
            //保存日志
            saveLog(point, time, e.getCause() == null ? e.getMessage() :
                    e.getCause().getMessage(), true);
            throw e;
        }
    }

    private String getClassName(ProceedingJoinPoint joinPoint) {
        Object proxy = joinPoint.getThis();
        Class<?> targetClass = proxy.getClass();
        return targetClass.getName();
    }

    private void saveLog(ProceedingJoinPoint joinPoint, long time, Object result,
                         boolean isError) {
        try {
            JacksonUtil.getInstance().setDateFormat(new SimpleDateFormat(DATE_FORMAT));
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            LogPo logPo = new LogPo();
            LogOperation annotation = method.getAnnotation(LogOperation.class);
            if (annotation != null) {
                //注解上的描述
                logPo.setLogType(isError ? LogType.error.getValue()
                        : annotation.type().getValue());
            }
            logPo.setCreateTime(new Date());
            Thread thread = Thread.currentThread();
            logPo.setThread(thread.getName());
            //请求参数
            Object[] args = joinPoint.getArgs();
            if (args == null || args.length == 0) {
                logPo.setParam("");
            } else {
                try {
                    logPo.setParam(JacksonUtil.toJsonString(args));
                } catch (Exception ignored) {
                    logPo.setParam("");
                }
            }
            logPo.setClassName(getClassName(joinPoint));
            logPo.setMethod(method.getName());
            logPo.setTime(String.valueOf(time));
            try {
                logPo.setResult(result == null ? "" : JacksonUtil.toJsonString(result));
            } catch (Exception ignored) {
                logPo.setResult("");
            }
            if (RequestContextHolder.getRequestAttributes() != null) {
                HttpServletRequest request = ((ServletRequestAttributes)
                        RequestContextHolder.getRequestAttributes()).getRequest();
                logPo.setIp(IpUtil.getIpAddress(request));
                String[] headerKey;
                //设置请求头部
                if (annotation != null && annotation.isHeaderSelf()) {
                    headerKey = annotation.headerKey();
                } else {
                    headerKey = LogConstants.saveHeaderKey;
                }
                if (ObjectUtils.isEmpty(headerKey)) {
                    logPo.setHeader("");
                } else {
                    try {
                        Map<String, String> header = LogLibUtils.getHeaders(request, headerKey);
                        logPo.setHeader(JacksonUtil.toJsonString(header));
                    } catch (Exception ignored) {
                        logPo.setHeader("");
                    }
                }
            }
            String fileName = LogConstants.LOG_COLLECTION_NAME;
            if (annotation != null && !ObjectUtils.isEmpty(annotation.saveFileName())) {
                fileName = annotation.saveFileName();
            }
            logService.saveLog(fileName, logPo);
        } catch (Exception ignored) {
        }
    }
}
